Vue2源码解读之Observer篇

您所在的位置:网站首页 new的作用 前端 Vue2源码解读之Observer篇

Vue2源码解读之Observer篇

2023-10-02 01:29| 来源: 网络整理| 查看: 265

今天团队内部分享和讨论了 Vue 数据是如何驱动视图的,本次主要讨论 Object 的变化侦测。

监听数据变化 收集依赖 通知依赖更新 监听数据变化 需要监听哪些数据的变化?

根组件data

子组件data 和 props 也就是说只有在 data 和 props 中定义的数据,才能被监听。

什么时候监听数据的变化

创建 Vue 实例的时候,initData阶段, 调用 observe 方法进行劫持数据变化。

function initData (vm: Component) { let data = vm.$options.data // observe data observe(data, true /* asRootData */) } 如何追踪数据变化

把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property

在 initData 阶段, Vue 会调用 observe 方法,内部会 new Observer(), 返回一个 ob对象, ob 对象是一个被 Object.defineProperty 方法劫持的对象。

Observer构造器中主要是缓存 ob 对象, 把实例缓存在 value._ob_ 中, 如果存在 value._ob_ 属性, 则不会 new Observer ,而是从缓存中获取 ob 实例, 减少重复创建。 这里 vue 用了一个 if 判断, 如果 observe 方法的参数是一个数组类型,则会遍历数组,让数组中的每一项都被 observe 方法调用,这样就可以劫数组中对象了。

observe(data, true /* asRootData */) function observe (value, asRootData) { if (!isObject(value) || value instanceof VNode) { return } let ob if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__ } else { ob = new Observer(value) } return ob } class Observer { constructor (value) { value.__ob__ = this if (Array.isArray(value)) { this.observeArray(value) } else { this.walk(value) } } // Vue 将遍历此对象所有的 property, 并绑定 defineReactive walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]) } } observeArray (items) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } }

使用 Object.defineProperty 把这些 property 全部转为 getter/setter, Vue 内部会通过 getter/setter 进行追踪依赖, 在 property 被修改时和被访问时通知变更。 调用 defineReactive 时, 还会递归遍历此对象下所有的 property, 绑定 defineReactive

function defineReactive (obj,property,val,customSetter,shallow) { observe(val) Object.defineProperty(obj, property, { get() { return val }, set(newVal) { val = newVal } }) } 收集依赖 依赖是什么?

谁用到了数据谁就是依赖,数据就是上文 Object.defineProperty 响应式劫持的 data, 依赖就是使用数据的地方。比如 template 模板里面使用的 data 变量。

为什么要收集依赖

数据变化了要更新视图,提前把跟这个数据有关的依赖收集起来,等到数据改变时, 可以从刚才收集里取出依赖,方便更新。

在何时收集依赖?

Object.defineProperty 的 getter 方法中收集依赖, 这里的依赖并不直接是一个视图, 而是一个 Watch 实例, 这个 watch 实例可以去通知视图更新的。

function defineReactive ( obj,property,val ) { const dep = new Dep() Object.defineProperty(obj, property, { get() { if (Dep.target) { dep.depend() } return value }, set: function reactiveSetter (newVal) { val = newVal dep.notify() } }) } class Dep { static target; constructor () { this.subs = [] } depend () { if (Dep.target) { Dep.target.addDep(this) } } notify () { const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } } class Watcher { constructor (vm,expOrFn){ this.getter = expOrFn this.newDeps = [] this.get() } get() { Dep.target = this this.getter.call(vm, vm) } addDep (dep) { this.newDeps.push(dep) } }

在 defineReactive 绑定每一个 property 之前, 会创建一个 Dep 实例, subs 里面是订阅的多个 watch, subs是一个数组, 一个 dep 可以订阅多个 watch 实例, 就是说一个数据改变了, 可能会影响多个视图更新。

在 getter 方法里, 有一个 Dep.target 参数, 这个 target 其实就是 watch 实例, 那么 target 从哪里来的呢。 在 beforeMount钩子 和 mounted 钩子初始化之间,会实例化 Watch 类, Watch 构造函数中会把 watch 实例保存在 Dep.target 上, 随后会触发所有数据的访问,也就是上面的 getter 方法,dep.depend() 会把 watch 保存起来, 这个过程就是收集依赖。

通知依赖更新 何时通知依赖


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3